﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OWLServer.owl.tipos;

namespace OWLServerQL.dominio
{
    /// <summary>
    /// Elemento que representa una comparación
    /// </summary>
    public class ElementoComparador : Elemento
    {
        private string literalIzquierdo;
        private string literalDerecho;
        private TipoComparacion tipoComparacion;

        /// <summary>
        /// Texto izquierdo de la compración
        /// </summary>
        public string LiteralIzquierdo
        {
            get { return literalIzquierdo; }
            set { literalIzquierdo = value; }
        }

        /// <summary>
        /// Texto izquierdo de la compración
        /// </summary>
        public string LiteralDerecho
        {
            get { return literalDerecho; }
            set { literalDerecho = value; }
        }

        /// <summary>
        /// Indica el tipo de comparación
        /// </summary>
        public TipoComparacion TipoComparacion
        {
            get { return tipoComparacion; }
            set { tipoComparacion = value; }
        }

        public ElementoComparador()
            : base()
        {
        }

        public ElementoComparador(string literalIzquierdo, string literalDerecho, TipoComparacion tipoComparacion, bool negado)
            : base(negado)
        {
            this.LiteralIzquierdo = literalIzquierdo;
            this.literalDerecho = literalDerecho;
            this.tipoComparacion = tipoComparacion;
        }

        public ElementoComparador(string literalIzquierdo, string literalDerecho, TipoComparacion tipoComparacion)
            : base()
        {
            this.LiteralIzquierdo = literalIzquierdo;
            this.literalDerecho = literalDerecho;
            this.tipoComparacion = tipoComparacion;
        }

        override public String ToString()
        {
            StringBuilder bf = new StringBuilder();
            bf.Append("\nTipo Elemento: ");
            bf.Append(base.TipoElemento);
            bf.Append("\nNegado: ");
            bf.Append(base.Negado);
            bf.Append("\nLiteral Izquierdo: ");
            bf.Append(literalIzquierdo);
            bf.Append("\nLiteral Derecho: ");
            bf.Append(literalDerecho);
            bf.Append("\nTipo de comparacion: ");
            bf.Append(tipoComparacion);
            bf.Append("\nUnion Anterior: ");
            bf.Append(base.UnionAnterior);
            bf.Append("\n++++++++++++>\n");
            if (base.ElementoSiguiente != null)
            {
                bf.Append(base.ElementoSiguiente.ToString());
            }
            return bf.ToString();
        }

        /// <summary>
        /// Comopone la lista de individuos que se están buscando en la consulta
        /// </summary>
        /// <param name="individuosOntologia">Lista de individuos de la Onlología seleccionada</param>
        /// <returns></returns>
        override public List<Individuo> owlServerQLToIndividuos(TipoElemento tipoElemento, List<Individuo> individuosOntologia)
        {
            List<Individuo> resultado = null;
            base.owlServerQLToIndividuos(tipoElemento, individuosOntologia);
            List<Individuo> conjuntoIzquierda = componerMisIndividuos(TipoElemento, individuosOntologia);
            List<Individuo> conjuntoDerecha = ElementoSiguiente == null ? null : ElementoSiguiente.owlServerQLToIndividuos(TipoElemento, individuosOntologia);
            resultado = unionIndividuos(conjuntoIzquierda, conjuntoDerecha);

            return resultado;
        }

        /// <summary>
        /// Compone los individuos que concidan con el elemento
        /// </summary>
        /// <param name="tipoElemento">Tipo de elemento</param>
        /// <param name="individuosOntologia">Individuos de la Ontologia</param>
        /// <returns>Lista de individuos de este elemento</returns>
        private List<Individuo> componerMisIndividuos(TipoElemento tipoElemento, List<Individuo> individuosOntologia)
        {
            List<Individuo> resultado = new List<Individuo>();
            bool encontrado = false;
            int size;
            foreach (Individuo indi in individuosOntologia)
            {
                switch (tipoElemento)
                {
                    case dominio.TipoElemento.PROPIEDADES_DATO:
                        size = indi.PropiedadesDatos.Count;
                        if (size > 0)
                        {
                            for (int i = 0; i < size && !encontrado; i++)
                            {
                                encontrado = componerResultado(resultado, indi, indi.PropiedadesDatos[i].PropiedadDato.Id, indi.PropiedadesDatos[i].Valor, indi.PropiedadesDatos[i].Negada);
                            }
                        }
                        break;
                    case dominio.TipoElemento.PROPIEDADES_OBJETO:
                        size = indi.PropiedadesObjetos.Count;
                        for (int i = 0; i < size && !encontrado; i++)
                        {
                            encontrado = componerResultado(resultado, indi, indi.PropiedadesObjetos[i].PropiedadObjeto.Id, indi.PropiedadesObjetos[i].Individuo.Id, indi.PropiedadesObjetos[i].Negada);
                        }
                        break;
                    default:
                        break;
                }
                encontrado = false;
            }

            return resultado;
        }

        /// <summary>
        /// Compone el resultado de la busqueda de individuos
        /// </summary>
        /// <param name="resultado">Resultado que estamos componiendo</param>
        /// <param name="indi">Individuo que estamos analizando</param>
        /// <param name="propiedad">Nombre de la propiedad</param>
        /// <param name="valor">Valor asignado a la propiedad</param>
        /// <param name="propiedadNegada">Indica si la propiedad esta negada o no</param>
        /// <returns>Indica si se ha encontrado un individuo que se ha insertado en el resultado</returns>
        private bool componerResultado(List<Individuo> resultado, Individuo indi, string propiedad, string valor, bool propiedadNegada)
        {
            bool encontradoIndividuo = false;

            if (LiteralIzquierdo == null) // Si ALL comparacion 'valor'
            {
                if (esVerdaderaComparacion(valor, propiedadNegada))
                {
                    insertarIndividuo(resultado, indi);
                    encontradoIndividuo = true;
                }
            }
            else if (LiteralDerecho == null) // Si 'propiedad' comparacion ALL
            {
                if (propiedad.Equals(LiteralIzquierdo))
                {
                    insertarIndividuo(resultado, indi);
                    encontradoIndividuo = true;
                }
            }
            else // Si 'propiedad' comparacion 'valor'
            {
                if (propiedad.Equals(LiteralIzquierdo))
                {
                    if (Negado)
                    {
                        if (!esVerdaderaComparacion(valor, propiedadNegada))
                        {
                            insertarIndividuo(resultado, indi);
                            encontradoIndividuo = true;
                        }
                    }
                    else
                    {
                        if (esVerdaderaComparacion(valor, propiedadNegada))
                        {
                            insertarIndividuo(resultado, indi);
                            encontradoIndividuo = true;
                        }
                    }
                }
            }

            return encontradoIndividuo;
        }

        /// <summary>
        /// Indica si es verdadera la compración de los elementos
        /// </summary>
        /// <param name="elementoIzq">Elemento de la parte izquierda de la comparación</param>
        /// <param name="propiedadNegada">Indica si la propiedad esta negada o no</param>
        /// <returns></returns>
        private bool esVerdaderaComparacion(String elementoIzq, bool propiedadNegada)
        {
            bool verdadera = false;

            switch (TipoComparacion)
            {
                case dominio.TipoComparacion.DISTINTO:
                    verdadera = !elementoIzq.Equals(LiteralDerecho);
                    break;
                case dominio.TipoComparacion.IGUAL:
                    verdadera = elementoIzq.Equals(LiteralDerecho);
                    break;
                case dominio.TipoComparacion.MAYOR_IGUAL_QUE:
                    verdadera = (elementoIzq.CompareTo(LiteralDerecho) >= 0);
                    break;
                case dominio.TipoComparacion.MAYOR_QUE:
                    verdadera = (elementoIzq.CompareTo(LiteralDerecho) > 0);
                    break;
                case dominio.TipoComparacion.MENOR_IGUAL_QUE:
                    verdadera = (elementoIzq.CompareTo(LiteralDerecho) <= 0);
                    break;
                case dominio.TipoComparacion.MENOR_QUE:
                    verdadera = (elementoIzq.CompareTo(LiteralDerecho) < 0);
                    break;
            }

            if (propiedadNegada)
            {
                verdadera = !verdadera;
            }

            return verdadera;
        }
    }
}
